﻿namespace AlohaAlgorithm
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;

    using Shared;

    public class AlohaAlgorithm : IAlgorithm
    {
        private readonly IDistribution clientsDistribution;

        private readonly IDictionary<Frame, bool> sentFrames;

        public AlohaAlgorithm(IDistribution clientsDistribution)
        {
            this.clientsDistribution = clientsDistribution;
            this.sentFrames = new Dictionary<Frame, bool>();
            this.Frames = new Stack<Frame>();

            this.AllowedRetries = 16;
            this.FrameSizeInKilobytes = 1;
            this.WaitTime = 0;
        }

        public int AllowedRetries { get; private set; }

        public double FrameSizeInKilobytes { get; private set; }

        public Stack<Frame> Frames { get; private set; }

        public int WaitTime { get; protected set; }

        public double Send(Frame frame, int numberOfClients, double timeDelta, Action<int, double> sendProgressCallback)
        {
            Trace.Indent();

            int framesRetries = 0;
            double time = 0;

            int frameNumber = 0;

            do
            {
                frameNumber++;
                Trace.WriteLine(string.Format("Transmitting Frame Number: {0}", frameNumber), "Aloha");

                time += timeDelta;
                time += this.WaitTime;

                int progress = (frameNumber * 100) / (int)frame.Size;

                if (sendProgressCallback != null)
                {
                    sendProgressCallback.Invoke(progress, time);
                }

                double totalClientsTryingToTransmit = this.clientsDistribution.NextDouble(1, numberOfClients + 1);

                Trace.WriteLine(string.Format("Clients Trying to Transmit: {0}", totalClientsTryingToTransmit), "Aloha");

                if (totalClientsTryingToTransmit != 1)
                {
                    framesRetries++;
                    Trace.WriteLine(string.Format("Frame Retries: {0}", framesRetries), "Aloha");
                    frameNumber = 0;
                    Trace.WriteLine(string.Format("Reset Frame Number"), "Aloha");
                }
            }
            while (frameNumber < frame.Size && framesRetries < this.AllowedRetries);

            bool sent = true;
            Trace.WriteLine(string.Format("Time: {0}", time), "Aloha");
            Trace.Unindent();

            if (framesRetries == this.AllowedRetries)
            {
                time = int.MaxValue;
                sent = false;
            }

            this.sentFrames[frame] = sent;

            return time;
        }

        public bool WasTransmissionSuccessful(Frame frame)
        {
            if (!this.sentFrames.ContainsKey(frame))
            {
                return false;
            }

            return this.sentFrames[frame];
        }

        public void SetupFrames(double kilobytesToTransmit)
        {
            this.Frames.Push(new Frame(kilobytesToTransmit));
        }

        public override string ToString()
        {
            return "ALOHA";
        }
    }
}